#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <dirent.h>
#include <errno.h>

#define BUFSIZE	128

char file_type(mode_t mode);
char *file_group_name(gid_t gid);
int file_nlink(nlink_t n);
int file_mode(mode_t mode, char *buf, int size);
long file_size(off_t size);
char *file_user_name(uid_t uid);
char *file_mtime(struct timespec st_mtim);

void mystat(const char *path);

/*
 * struct stat 结构体用于存储文件或文件系统的状态信息。
 * 它包含以下字段：
 *
 * - st_dev: 文件所在设备的 ID。
 * - st_ino: 文件的 inode 号，唯一标识文件。
 * - st_mode: 文件的类型和权限（如 S_IFREG 表示普通文件，S_IRUSR 表示用户可读）。
 * - st_nlink: 文件的硬链接数。
 * - st_uid: 文件所有者的用户 ID。
 * - st_gid: 文件所有者的组 ID。
 * - st_rdev: 设备文件的设备 ID（仅对设备文件有效）。
 * - st_size: 文件的大小（字节数）。
 * - st_blksize: 文件系统的块大小（用于高效 I/O 操作）。
 * - st_blocks: 文件占用的块数（每块通常为 512 字节）。
 *
 * - st_atim: 最后访问时间（包括秒和纳秒）。
 * - st_mtim: 最后修改时间（包括秒和纳秒）。
 * - st_ctim: 最后状态变更时间（包括秒和纳秒）。
 *
 * 以下是宏
 * - st_atime: 最后访问时间（秒，旧版字段）。
 * - st_mtime: 最后修改时间（秒，旧版字段）。
 * - st_ctime: 最后状态变更时间（秒，旧版字段）。
 */


void __show_stat(const char *path, const char *name) 
{
    struct stat res;
    char str[BUFSIZE] = {};

    if (-1 == stat(path, &res)) {
        perror("stat()");
        return;
    }

    // 文件的类型 权限 res.st_mode
    file_mode(res.st_mode, str, BUFSIZE);
    printf("%c%s %d %s %s %ld %s %s\n", file_type(res.st_mode), str, \
            file_nlink(res.st_nlink), file_user_name(res.st_uid), \
            file_group_name(res.st_gid), file_size(res.st_size), \
            file_mtime(res.st_mtim), name);

}

void mystat(const char *path)
{
    struct stat res;
    char str[BUFSIZE] = {};
    DIR *dp = NULL;
    struct dirent *entry = NULL;
    char buf[BUFSIZE] = {0};

    if (-1 == stat(path, &res)) {
        perror("stat()");
        return;
    }

    // 是否为目录
    if (!S_ISDIR(res.st_mode)) {
        printf("%s\n", path);
        return;
    }

    dp = opendir(path);
    if (NULL == dp) {
        perror("opendir()");
        return;
    }

    while (1) {
        errno = 0;
        entry = readdir(dp);
        if (NULL == entry) {
            if (errno) {
                printf("%s\n", path);
                perror("readir()");
                closedir(dp);
                return;
            }
            break;
        }

        memset(buf, '\0', BUFSIZE);
        strcpy(buf, path);
        strcat(buf, "/");
        strcat(buf, entry->d_name);
        __show_stat(buf, entry->d_name);
    }

    closedir(dp);
}


/* 文件类型（7种）f/- d c b l s p */
char file_type(mode_t mode)
{
    char res;

    // if ((mode & S_IFMT) == S_IFREG)

    switch (mode & S_IFMT) {
        case S_IFBLK:  
            // printf("block device\n");  
            res = 'b';
            break;
        case S_IFCHR:  
            // printf("character device\n");
            res = 'c';
            break;
        case S_IFDIR: 
            // printf("directory\n");   
            res = 'd';
            break;
        case S_IFIFO:  
            // printf("FIFO/pipe\n");
            res = 'p';
            break;
        case S_IFLNK: 
            // printf("symlink\n");  
            res = 'l';
            break;
        case S_IFREG: 
            // printf("regular file\n");  
            res = '-';
            break;
        case S_IFSOCK: 
            // printf("socket\n");  
            res = 's';
            break;
        default:    
            // printf("unknown?\n");  
            res = '?';
            break;
    }

    return res;
}

/* 文件权限 3组 rwx 9位 */
int file_mode(mode_t mode, char *buf, int size)
{
    int mode_value[] = {S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, \
        S_IWGRP, S_IXGRP, S_IROTH, S_IWOTH, S_IXOTH};
    int i;

    if (size < 10)
        return -1;

    strcpy(buf, "rwxrwxrwx");

    for (i = 0; i < 9; i++) {
        if (!(mode & mode_value[i]))
            buf[i] = '-';
    }

    return 0;
}

/* 文件字节大小 */
long file_size(off_t size)
{
    return size;
}

/* 文件硬链接个数 */
int file_nlink(nlink_t n)
{
    return n;
}

/* 文件拥有者 */
char *file_user_name(uid_t uid)
{
    // 用户信息在 /etc/passwd 文件内
    // getpwuid函数返回一个地址，而且有同名的getpwuid_r函数，说明返回的地址数据在静态区
    struct passwd *pw = getpwuid(uid); // 根据uid获取用户信息
    if (NULL == pw)
        return NULL;
    return pw->pw_name;
}

/* 文件所属组 */
char *file_group_name(gid_t gid)
{
    // 组信息在 /etc/group 文件内
    struct group *gr = getgrgid(gid); // 根据gid获取组信息
    if (NULL == gr)
        return NULL;
    return gr->gr_name;
}

/* 文件修改时间 */
char *file_mtime(struct timespec st_mtim)
{
    static char buf[BUFSIZE] = {};

    struct tm *tmp = localtime(&st_mtim.tv_sec); // 根据时间戳得到时间结构体
    strftime(buf, BUFSIZE, "%m月 %d %H:%M", tmp); // 将时间结构体解析为结构化的时间字符串

    return buf;
}



